/*
Copyright 2013-present Barefoot Networks, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

#ifndef IR_ID_H_
#define IR_ID_H_

#include "lib/cstring.h"
#include "lib/exceptions.h"
#include "lib/hash.h"
#include "lib/source_file.h"

namespace P4::IR {

// An identifier.
struct ID : Util::IHasSourceInfo, public IHasDbPrint {
    Util::SourceInfo srcInfo;
    cstring name = nullptr;
    // We save the original name to show the user on error messages.
    // If originalName is nullptr the symbol has been generated by the compiler.
    cstring originalName = nullptr;
    ID() = default;
    ID(Util::SourceInfo si, cstring n, cstring o) : srcInfo(si), name(n), originalName(o) {
        if (n.isNullOrEmpty()) BUG("Identifier with no name");
    }
    ID(Util::SourceInfo si, cstring n) : srcInfo(si), name(n), originalName(n) {
        if (n.isNullOrEmpty()) BUG("Identifier with no name");
    }
    // FIXME: Replace with single string_view constructor
    ID(const char *n) : ID(Util::SourceInfo(), cstring(n)) {}  // NOLINT(runtime/explicit)
    ID(cstring n) : ID(Util::SourceInfo(), n) {}               // NOLINT(runtime/explicit)
    ID(const std::string &n) : ID(Util::SourceInfo(), n) {}    // NOLINT(runtime/explicit)
    ID(cstring n, cstring old) : ID(Util::SourceInfo(), n, old) {}
    void dbprint(std::ostream &out) const override {
        out << name;
        if (originalName != nullptr && originalName != name) out << "/" << originalName;
    }
    bool operator==(const ID &a) const { return name == a.name; }
    bool operator!=(const ID &a) const { return name != a.name; }
    /// Defer to cstring's notion of less, which is a lexicographical and not a pointer comparison.
    bool operator<(const ID &a) const { return name < a.name; }
    bool operator==(cstring a) const { return name == a; }
    bool operator!=(cstring a) const { return name != a; }
    /// Defer to cstring's notion of less, which is a lexicographical and not a pointer comparison.
    bool operator<(cstring a) const { return name < a; }
    bool operator==(const char *a) const { return name == a; }
    bool operator!=(const char *a) const { return name != a; }
    /// Defer to cstring's notion of less, which is a lexicographical and not a pointer comparison.
    bool operator<(const char *a) const { return name < a; }
    explicit operator bool() const { return name.c_str() != nullptr; }
    operator cstring() const { return name; }
    std::string string() const { return name.string(); }
    std::string_view string_view() const { return name.string_view(); }
    bool isDontCare() const { return name == "_"; }
    Util::SourceInfo getSourceInfo() const override { return srcInfo; }
    cstring toString() const override { return originalName.isNullOrEmpty() ? name : originalName; }

    /// Helper to simplify usage of ID in Abseil functions (e.g. StrCat / StrFormat, etc.) without
    /// explicit string_view conversion.
    template <typename Sink>
    friend void AbslStringify(Sink &sink, const ID &id) {
        sink.Append(id.string_view());
    }
};

}  // namespace P4::IR

namespace P4::Util {
template <>
struct Hasher<IR::ID> {
    size_t operator()(const IR::ID &id) const { return Util::Hash{}(id.name); }
};
}  // namespace P4::Util

#endif /* IR_ID_H_ */
